import 'package:family_expense/features/journey/models/journey_income_expense_detail.dart';
import 'package:family_expense/features/journey/services/journey_income_expense_detail_service.dart';
import 'package:flutter/material.dart';
import 'package:my_flutter_baidu_map/my_flutter_baidu_map.dart';
import 'package:my_flutter_core/my_flutter_core.dart';

class JourneyFootmark extends StatefulWidget {
  const JourneyFootmark({super.key});

  @override
  State<JourneyFootmark> createState() => _JourneyFootmarkState();
}

class _JourneyFootmarkState extends State<JourneyFootmark> {
  // Map markers.
  final List<BMFMarker> _markers = [];

  // Stores formatted addresses.
  final Map<String, String> _markerNames = <String, String>{};

  // For displaying bottom sheet for user interaction.
  final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();

  // For query distinct year list and income/expense associated coordinates.
  final JourneyIncomeExpenseDetailService _incomeExpenseDetailService =
      JourneyIncomeExpenseDetailService();

  final List<String> _years = [];
  final String _all = '全部';

  // For locating user position.
  late final MapLocationService _mapLocationService = MapLocationService(
    locationChanged: _onLcationChanged,
    coordType: BMFLocationCoordType.wgs84,
  );

  // Indicates if currently positioning.
  bool _isPositioning = false;

  String _selectedAddress = '';
  String _selectedYear = getCurrentDateTime().year.toString();

  // Loaded data list.
  List<JourneyIncomeExpenseDetail> _list = [];

  // Map controller for moving camera.
  BMFMapController? _bmfMapController;

  // For displaying bottom sheet for location address info.
  PersistentBottomSheetController? _addressBottomSheetController;

  // For displaying bottom sheet for year selection.
  PersistentBottomSheetController? _yearBottomSheetController;

  @override
  void initState() {
    super.initState();

    _loadYears();
  }

  @override
  void dispose() {
    _mapLocationService.dispose();

    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: _scaffoldKey,
      appBar: _buildAppBar(),
      floatingActionButton: _buildFloatingActionButton(),
      body: _buildMap(),
    );
  }

  /// Builds app bar.
  AppBar _buildAppBar() {
    return AppBar(
      title: Text(_isPositioning ? '正在定位...' : '地图足迹'),
      actions: [
        TextButton(
          onPressed: _showYearList,
          child: Text(
            _selectedYear,
            style: TextStyle(
              color: calculateContrastingColor(
                Theme.of(context).appBarTheme.backgroundColor!,
              ),
            ),
          ),
        ),
      ],
    );
  }

  /// Builds floating action button.
  Widget _buildFloatingActionButton() {
    return Column(
      mainAxisAlignment: MainAxisAlignment.end,
      children: <Widget>[
        FloatingActionButton(
          onPressed: !_isPositioning
              ? () {
                  if (mounted) {
                    setState(() => _isPositioning = true);
                  }

                  // _mapLocationService.startLocation();
                }
              : null,
          child: const Icon(Icons.gps_fixed),
        ),
        const SizedBox(height: 60.0),
      ],
    );
  }

  /// Builds map widget.
  Widget _buildMap() {
    return BMFMapWidget(
      onBMFMapCreated: _onBMFMapCreated,
      mapOptions: _getMapOptions(),
    );
  }

  /// Sets up callbacks once map has been created.
  void _onBMFMapCreated(BMFMapController controller) {
    _bmfMapController = controller;
    _bmfMapController!.setMapDidLoadCallback(callback: _initLoad);
    _bmfMapController!.setMapClickedMarkerCallback(callback: _tapMarker);
  }

  /// Shows input coordiate or starts locating once map has been loaded.
  Future<void> _initLoad() async {
    if (mounted) {
      setState(() => _isPositioning = true);
    }

    _mapLocationService.startLocation();

    // Queries all related location coordinates.
    _list = await _incomeExpenseDetailService.queryCoordinates(_selectedYear);

    _setMarkers();
  }

  void _tapMarker(BMFMarker marker) {
    _showLocationInfo(_markerNames[marker.id]!);
  }

  /// Gets map options.
  BMFMapOptions _getMapOptions() {
    return BMFMapOptions(
      center: MapService.loadCachedLastCoordinates(),
      zoomLevel: 9,
      rotateEnabled: false,
      showZoomControl: false,
    );
  }

  /// Listens on location change event and moves camera.
  Future<void> _onLcationChanged(MapLocation location) async {
    // Moves carmera to this coordinate.
    _bmfMapController?.setCenterCoordinate(
      BMFCoordinate(location.latitude!, location.longitude!),
      true,
    );

    if (mounted) {
      setState(() => _isPositioning = false);
    }
  }

  /// Sets markers on the map based on all coordiates.
  void _setMarkers() {
    _markers.clear();
    _markerNames.clear();

    // Adds all markers.
    for (final p in _list) {
      final marker = BMFMarker.icon(
        position: BMFCoordinate(p.latitude!, p.longitude!),
        icon: MapConfig.greenMarkerIconName,
      );

      _markers.add(marker);
      _markerNames[marker.id] = p.formattedAddress ?? '未知';
    }

    _bmfMapController!.cleanAllMarkers();
    _bmfMapController!.addMarkers(_markers);
  }

  /// After getting location info we need to display it on bottom sheet.
  ///
  /// [name] will be available for [onPoiTouched] event or [widget.location].
  void _showLocationInfo(String name) {
    // IMPORTANT!!!
    // Uses [name] in [Text] directly would NOT make UI change after calls _addressBottomSheetController?.setState!(() {}).
    // That's why we need a class member instead.
    _selectedAddress = name;

    _yearBottomSheetController?.close();

    // Displays formatted address on bottom sheet.
    if (_addressBottomSheetController == null) {
      _addressBottomSheetController = _scaffoldKey.currentState!
          .showBottomSheet(
            (context) => getSelectionBottomSheetContent([
              Expanded(child: Text(_selectedAddress)),
              TextButton(
                onPressed: _addressBottomSheetController!.close,
                child: const Text('关闭', style: TextStyle(color: Colors.blue)),
              ),
            ]),
          );

      // When bottom sheet is closed then we need to clear it.
      _addressBottomSheetController!.closed.whenComplete(
        () => _addressBottomSheetController = null,
      );
    } else {
      // If bottom sheet already opens we only need to refresh its content.
      _addressBottomSheetController!.setState!(() {});
    }
  }

  /// Queries data and sets markers.
  Future<void> _setMarkerWithData(String year) async {
    _selectedYear = year;

    _list = await _incomeExpenseDetailService.queryCoordinates(
      _selectedYear == _all ? '' : _selectedYear,
    );

    _setMarkers();

    if (mounted) {
      setState(() {});

      // Closes opened bottom sheet.
      NavigatorHelper.pop(context);
    }
  }

  /// Loads current all distinct years.
  Future<void> _loadYears() async {
    final years =
        (await _incomeExpenseDetailService.queryExistingDistinctYears()).map(
          (e) => e.toString(),
        );

    _years.addAll(years);
  }

  /// Shows year list for selection.
  Future<void> _showYearList() async {
    _addressBottomSheetController?.close();

    final items = _years
        .map(
          (e) => ListTile(
            title: Text(e),
            selected: e.toString() == _selectedYear,
            onTap: () => _setMarkerWithData(e.toString()),
          ),
        )
        .toList();

    items.insert(
      0,
      ListTile(
        title: Text(_all),
        selected: _selectedYear == _all,
        onTap: () => _setMarkerWithData(_all),
      ),
    );

    _yearBottomSheetController ??= await showModalBottomSheet(
      context: context,
      builder: (context) =>
          getSelectionBottomSheetContent(items, isHoriz: false),
    );
  }
}
